<?php 
/**
* @package DomDB.Test
* @author Oliver Leisalu
*/
final class DomDBTester {
	
	public static $messages = array(
		'testExistance' => array(
			1 => 'Object(id:%s) exists',
			2 => 'Object(id:%s) doesn\'t exist in table %s! ',
		),
		'testValue' => array(
			1 => 'Values match!',
			2 => 'Object(id:%s) property value is not what expected. Got "%s" but expected "%s"! ',
		),
		'testParent' => array(
			1 => 'Child is parent child',
			2 => 'Child(id:%s) is not parent(id:%s) child',
			3 => 'Child(id:%s) is parent(id:%s) child, but parentField value is not correct!',
			4 => 'Parent(id:%s) doesnt exists!',
			5 => 'Child(id:%s) doesnt exists!'
		),
		'testLink' => array(
			1 => 'Child is linked with parent',
			2 => 'Child(id:%s) is not parent(id:%s) sibling',
			3 => 'Child(id:%s) is related to parent(id:%s) as parent not as sibling',
			4 => 'Parent(id:%s) doesnt exist!',
			5 => 'Child(id:%s) doesnt exists!'
		),
		'testPair' => array(
			1 => 'Values match',
			2 => 'Values dont match (%s != %s)'
		)
	);

	public static function runTestM( $class, $method, $name ) {
		$time = microtime(true);
		$result  = call_user_func( array($class, $method));
		return self::parseTestResult( $name, $result, round(microtime(true)-$time,5) );
	}

	public static function runTest( array $result, $name = 'Test' ) {
		return self::parseTestResult( $name, $result );
	}
	
	//-------------------------------------------------------------------
	// Testing methods
	//-------------------------------------------------------------------
	
	
	/**
	* @param Int $e expected result
	* @param Int $id Object id
	* @param String $tableName
	* @return Array( Boolean $pass, Int $resultCode, String $message)
	* Possible result codes
	* 1 - Object exists
	* 2 - Object doesnt exist
	*/
	public static function testExistance( $e, $id ) {
		// tests if object exists in main table
		$ob = DomSql::selectOneQuery('SELECT id, tableName FROM domdb_objects WHERE id = '.(int)$id.' LIMIT 1');
		if( !$ob['id'] ) {
			return self::message( 'testExistance', $e, 2, array($id, 'domdb_objects') );
		}
		
		// tests if object exists in it real table
		if( $ob['tableName'] ) {
			$ob2 = DomSql::selectOneQuery('SELECT id FROM '.$ob['tableName'].' WHERE id = '.(int)$id.' LIMIT 1');
			if( !$ob2['id'] ) {
				return self::message('testExistance', $e, 2, array($id, $ob['tableName'] ) );
			}
		}
		return self::message('testExistance', $e, 1);
	}
	
	/**
	* @param Int $e expected result
	* @param Int $id Object id
	* @param String $var database field name
	* @param String $value value to match
	* @return Array( Boolean $pass, Int $resultCode, String $message)
	* Possible result codes
	* 1 - Values match
	* 2 - Values dont match
	* 3 - Object doesnt exist
	*/
	public static function testValue( $e, $id, $var, $value ) {
		$existTest = self::testExistance( 1, $id );
		if( $existTest[0] == false ) {
			$existTest[1] = 3;
			return $existTest;
		}
		$parent = DomSql::selectOneQuery('SELECT tableName FROM domdb_objects WHERE id = '.(int)$id.' LIMIT 1');
		$test = DomSql::selectOneQuery('SELECT '.$var.' as n FROM '.$parent['tableName'].' WHERE id = '.(int)$id.' lIMIT 1');
		if( $test['n'] != $value ) {
			return self::message('testValue', $e, 2, array($id, $test['n'], $value) );
		}
		return self::message('testValue',$e, 1);
	}
	
	/**
	* @param Int $e expected result
	* @param Int $id Object id
	* @param String $field - in which field object is held
	* @param Int $childId Object child id
	* @return Array( Boolean $pass, Int $resultCode, String $message)
	* Possible result codes
	* 1 - Ok
	* 2 - Object is noy linked to parent
	* 3 - Object is linked to child, but parentField value is not correct!
	* 4 - Parent doesnt exist
	* 5 - Child doesnt exist
	*/
	public static function testParent( $e, $id, $field = NULL, $childId  ) {
		$parent = DomSql::selectOneQuery('SELECT id FROM domdb_objects WHERE id = '.(int)$id.' LIMIT 1');
		$child = DomSql::selectOneQuery('SELECT id, parentId, parentField FROM domdb_objects WHERE id = '.(int)$childId.' LIMIT 1');

		if( !$parent['id'] ) {
			return self::message('testParent',$e, 4, array($id) );
		}
		
		if( !$child['id'] ) {
			return self::message('testParent',$e, 5, array($childId) );
		}
		
		if( $child['parentId'] != $parent['id'] ) {
			return self::message('testParent',$e, 2, array($id, $childId) );
		}
				
		if( $child['parentField'] != $field ) {
			return self::message('testParent',$e, 3, array($id, $childId) );
		}
		
		return self::message('testParent',$e, 1);
	}
	
	/**
	* @param Int $e expected result
	* @param Int $id Object id
	* @param String $field - in which field object is held
	* @param Int $childId Object child id
	* @return Array( Boolean $pass, Int $resultCode, String $message)
	* Possible result codes
	* 1 - Ok
	* 2 - Object is not linked to parent
	* 3 - Object is related as parent-child
	* 4 - Parent doesnt exist
	* 5 - Child doesnt exist
	*/
	public static function testLink( $e, $id, $field, $childId  ) {
		$parent = DomSql::selectOneQuery('SELECT id,className FROM domdb_objects WHERE id = '.(int)$id.' LIMIT 1');
		$bindingsTable = strtolower($parent['className'].'_'.$field.'_bindings');
		$child = DomSql::selectOneQuery('SELECT childId, parentId FROM '.$bindingsTable.' WHERE parentId = '.(int)$id.' AND childId = '.(int)$childId.' LIMIT 1');

		if( !$parent['id'] ) {
			return self::message('testLink',$e, 4, array($id) );
		}
		
		$childR = DomSql::selectOneQuery('SELECT id, parentField, parentId FROM domdb_objects WHERE id = '.(int)$childId.' LIMIT 1');
		if( !$childR['id'] ) {
			return self::message('testLink',$e, 5, array($childId) );
		}
		
		if( !$child['parentId'] ) {
			return self::message('testLink',$e, 2, array($id, $childId) );
		}
		
		
		if( $childR['parentId'] == $id && $childR['parentField'] == $field ) {
			return self::message('testLink',$e, 3, array($id, $childId) );
		}
		
		return self::message('testLink',$e, 1);
	}
	
	/**
	* @param Int $e expected result
	* @param Mixed $value1
	* @param Mixed $value2
	* @return Array( Boolean $pass, Int $resultCode, String $message)
	* Possible result codes
	* 1 - Values match
	* 2 - Values dont match
	*/
	public static function testPair( $e, $value1, $value2 ) {
		if( $value1 == $value2 )
			return self::message('testPair', $e, 1);
		else
			return self::message('testPair', $e, 2, array($value1, $value2));
	}
	
	
	public static function fail( $message = '' ) {
		return array(false, 0, $message);
	}
			
	//-------------------------------------------------------------------
	// privates
	//-------------------------------------------------------------------
	
	private static function parseTestResult( $name, array $result, $time = 0 ) {
		$str = '<div style="font-family:Arial; font-size:12px; margin-bottom: 10px; ">';
		if( self::analyze( $result ) == true ) {
			$str .= '<span style="color:#00CC00; font-weight: bold;">[ PASS ]</span> - <i>'.$name.'</i>' . ($time > 0 ? ' - '.$time.' sec' : NULL);
		} else {
			$str .= '<span style="color:#FF0000; font-weight: bold;">[ FAIL ]</span> - <i>'.$name.'</i>'. ($time > 0 ? ' - '.$time.' sec' : NULL).'<br />';
			foreach( $result as $i => $mes ) {
				if( $mes[0] == false ) {
					$str .= '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'.$i.') '.$mes[2].'<br />';
				}
			}
		}
		$str .= '</div>';
		return $str;
	}
	
	private static function parseResult( $message, array $params ) {
		$n = substr_count( $message, '%s');
		if( count($params) < $n) {
			for( $i= count($params); $i<$n; $i++) {
				$params[] = '';
			}
		}
		return vsprintf( $message, $params);
	}
	
	private static function message( $method, $expectedCode, $resultCode, array $params = array() ) {
		if( $expectedCode === $resultCode )
			return array( true, $resultCode, 'Pass' );
		else
			return array( false, $resultCode, self::parseResult( self::$messages[$method][$resultCode], $params ) );
		
	}
	
	/**
	* Checks result and tells if test is successful or not
	* @param Array $result
	* @return Boolean 
	*/
	private static function analyze( array &$result ) {
		foreach( $result as &$err ) {
			if( $err[0] == false ) return false;
		}
		return true;
	}

	//-------------------------------------------------------------------
	// DomDB default tests
	//-------------------------------------------------------------------
	
	public static function testDomDBDefault() {
		DomDB::install();
		$refl = new ReflectionClass('DomDBTestCases');
		foreach( $refl->getMethods() as $meth ) {
			echo self::testDomDBDefaultTest( $meth->name );
		}
	}
	
	public static function testDomDBDefaultTest( $name ) {
		echo self::runTestM( 'DomDBTestCases',$name , $name );
	}
	
	
	
}
?>